/*
    Modified from The Boost Library
    Modified by : 2013 darkcat

    (C) Copyright Nicolai M. Josuttis 2001.

    Distributed under the Boost Software License, Version 1.0. (See
    accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
*/

#pragma once

// nx...
#include "config/general.h"
// nx_assert
#include "bugfix/assert.h"
// nx_operator
#include "utility/operator.h"
// NX_SHIELD
#include "type/typetools.h"
// nx algorithm
#include "algorithm/algorithm.h"
// Special assign algorithm
#include "algorithm/assign.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

template <typename T, size_t N>
class array : nx_operator(typename NX_SHIELD(array<T, N>), Unequal, Comparable)
{
public:
    // type definitions
    typedef T               value_type;
    typedef T*              iterator;
    typedef const T*        const_iterator;
    typedef T&              reference;
    typedef const T&        const_reference;
    typedef size_t          size_type;
    typedef ptrdiff_t       difference_type;

public:
    // fixed-size array of elements of type T
    T elems_[N];
    
    // iterator support
    iterator        begin()                 { return elems_; }
    const_iterator  begin() const           { return elems_; }

    iterator        end()                   { return elems_ + N; }
    const_iterator  end() const             { return elems_ + N; }

public:
    // operator[]
    reference operator[](size_type i)
    {
        nx_assert(i < N);
        return elems_[i];
    }
    const_reference operator[](size_type i) const
    {
        nx_assert(i < N);
        return elems_[i];
    }

    // at()
    reference       at(size_type i)         { return elems_[i]; }
    const_reference at(size_type i) const   { return elems_[i]; }

    // front() and back()
    reference front()                       { return elems_[0]; }
    const_reference front() const           { return elems_[0]; }
    reference back()                        { return elems_[N - 1]; }
    const_reference back() const            { return elems_[N - 1]; }

    // size is constant
    static size_type size()                 { return N; }
    static bool empty()                     { return false; }
    static size_type max_size()             { return N; }

    // swap (note: linear complexity)
    void swap(array<T, N>& y)
    {
        for(size_type i = 0; i < N; ++i)
            nx::swap(elems_[i], y.elems_[i]);
    }

    // direct access to data (read-only)
    const T* data() const                   { return elems_; }
    T* data()                               { return elems_; }

    // use array as C array (direct read/write access to data)
    T* c_array()                            { return elems_; }

    // assignment with type conversion
    template <typename T2>
    array<T, N>& operator=(const array<T2, N>& rhs)
    {
        nx::copy(*this, rhs);
        return *this;
    }

    // assign one value to all elements
    void assign (const T& value)            { fill(value); }    // A synonym for fill
    void fill   (const T& value)            { nx::fill(*this, value); }

    // comparisons
    friend bool operator==(const array<T, N>& x, const array<T, N>& y)
    {
        return nx::equal(x, y);
    }
    friend bool operator< (const array<T, N>& x, const array<T, N>& y)
    {
        return nx::compare(x, y);
    }
};

/*
    Special swap algorithm
*/

template <typename T, size_t N>
void swap(array<T, N>& x, array<T, N>& y)
{
    x.swap(y);
}

/*
    Special assign algorithm
*/

template <typename T_, size_t N_>
struct Push<array<T_, N_> >
{
    typedef array<T_, N_> T;
    template <typename V>
    static void _(const V& val, typename TraitsContainer<T>::ite_t ite, T& /*set*/)
    { (*ite) = val; }
};

template <typename T_, size_t N_>
struct Pop<array<T_, N_> >
{
    typedef array<T_, N_> T;
    static void _(typename TraitsContainer<T>::ite_t /*ite*/, T& /*set*/)
    {}
};

template <typename T_, size_t N_>
struct Assign<array<T_, N_>, Push>
{
    typedef array<T_, N_> T;

    struct P_stream_
    {
        T* set_p;
        typename TraitsContainer<T>::ite_t iter;

        template <typename V> void _(const V& val)
        {
            nx_assert(set_p && iter);
            Push<T>::_(val, iter++, *set_p);
        }
    };

    class Stream_ : public Stream<P_stream_>
    {
    public:
        const Stream_& operator()(T* p)
        {
            Stream<P_stream_>::set_p = p;
            Stream<P_stream_>::iter  = begin(*p);
            return (*this);
        }

        template <typename K, typename V>
        const Stream_& operator()(const K& k, const V& v) const
        {
            (*this) << typename TraitsContainer<T>::pair_t(k, v);
            return (*this);
        }
    };

    typedef Stream_ stream_t;
};

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
